#pragma once
#include "common.hpp"


const int defbacklog = 8;

class Socket
{
public:

    virtual void GetSocket() = 0;
    virtual void GetListen(int backlog) = 0;
    virtual void GetBind(int port) = 0;
    virtual void GetConnect(std::string ip,uint16_t port) = 0;
    virtual int Accept(Inetaddr* addr) = 0;
    virtual int Getfd() = 0;
    
    void BuideTcpSocket(int port,int backlog = defbacklog)
    {
        GetSocket();
        GetBind(port);
        GetListen(backlog);
    }

    void BuideTcpClientSocket(std::string ip, uint16_t port)
    {
        GetSocket();
        GetConnect(ip,port);
    }
    virtual ~Socket(){}

private:
};


class TcpSocket : public Socket
{
public:
    TcpSocket():_solfd(-1)
    {}
    TcpSocket(int fd):_solfd(fd)
    {}

    int Recv(std::string* str)
    {
        char buff[2048];
        int rsz = recv(_solfd,buff,sizeof(buff),0);
        if(rsz > 0){
            buff[rsz] = 0;
            *str += buff;
        }
        return rsz;
    }

    int Send(std::string& str)
    {
        int wsz = send(_solfd,str.c_str(),str.size(),0);

        if(wsz < 0){
            LOG(level::DEBUG)<<"send false";
        }
        return wsz;
    }

    void GetConnect(std::string ip,uint16_t port) override
    {
        Inetaddr addr(ip,port);
        int n = connect(_solfd,addr.Getsockaddr(),addr.Lenaddr());
        if(n < 0){
            LOG(level::ERROR)<<"Connect false";
            exit(CONNECT_ERR);
        }
        LOG(level::INFO)<<"Connect success";
    }

    void GetSocket() override
    {
        _solfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_solfd < 0)
        {
            LOG(level::FATAL) << "socket false";
            exit(SOCKET_ERR);
        }
        LOG(level::INFO) << "socket success";
    }

    void GetListen(int backlog) override
    {
        if (listen(_solfd, backlog) < 0)
        {
            LOG(level::FATAL) << "listen false";
            exit(LISTEN_ERR);
        }
        LOG(level::INFO) << "listen success";
    }

    void GetBind(int port) override
    {
        Inetaddr addr(port);
        if(bind(_solfd,addr.Getsockaddr(),addr.Lenaddr()) < 0)
        {
            LOG(level::FATAL) << "bind false";
            exit(BIND_ERR);
        }
        LOG(level::INFO) << "bind success";
    }

    int Accept(Inetaddr* addr) override
    {   
        struct sockaddr_in peer;
        int len = sizeof(peer);
        int tmpfd = accept(_solfd,TOSOCKADDR(peer),(socklen_t*)&len);
        if(tmpfd < 0)
        {
            LOG(level::WARING)<<"accept false";
            exit(ACCEPT_ERR);
        }
            LOG(level::INFO)<<"accept success";
            addr->Setaddr(peer);
            return tmpfd;
    }

    int Getfd() 
    {
        return _solfd;
    }

    ~TcpSocket(){}

private:
    int _solfd; // sock or listenfd
};